package com.yzd.learnjava.enumerated;

import java.util.EnumMap;

import com.yzd.learnjava.util.Generator;
import com.yzd.learnjava.util.Print;

public class VendingMachine {
	private static int amount = 0;
	private static State state = State.RESTING;
	private static Input selection = null;
	enum StateDuration{
		TRANSIENT
	}
	enum State{
		RESTING{
			void next(Input in) {
				switch (Category.categorize(in)) {
				case MONEY:
					amount += in.amount();
					state = State.ADDING_MONEY;
					break;
				case SHUT_DOWN:
					state = TERMINAL;
					break;
				default:
				}
			}
		},
		ADDING_MONEY{
			void next(Input in) {
				switch (Category.categorize(in)) {
				case MONEY:
					amount += in.amount();
					state = State.ADDING_MONEY;
					break;
				case TIME_SELECTION:
					selection = in;
					if(amount < selection.amount()) {
						Print.print("Insufficient money for " + selection);
					} else {
						state = State.DISPENSING;
					}
					break;
				case QUIT_TRANSACTION:
					state = State.GIVING_CHANGE;
					break;
				case SHUT_DOWN:
					state = State.TERMINAL;
					break;
				default:
				}
			}
		},
		DISPENSING(StateDuration.TRANSIENT){
			void next() {
				Print.print("here is your " + selection);
				amount -= selection.amount();
				state = State.GIVING_CHANGE;
			}
		},
		GIVING_CHANGE(StateDuration.TRANSIENT){
			void next() {
				if(amount > 0) {
					Print.print("Your change : " + amount);
					amount = 0;
				}
				state = RESTING;
			}
		},
		TERMINAL{
			void output() {
				Print.print("Halted");
			}
		};
		
		private boolean isTransient = false ;
		State(){}
		State(StateDuration trans){
			isTransient = true;
		}
		void next() {
			throw new RuntimeException("Only call next() for StateDuration.TRANSIENT states");
		}
		void next(Input input) {
			throw new RuntimeException("Only call next(Input input) for non-transient states");
		}
		void output() {
			Print.print(amount);
		}
	}
	
	static void run(Generator<Input> gen) {
		while(state != State.TERMINAL) {
			state.next(gen.next());
			while(state.isTransient) {
				state.next();
			}
			state.output();
		}
	}
	
	public static void main(String[] args) {
		run(new Generator<Input>() {
			public Input next() {
				return Input.randomSelection();
			}
		});
	}
}

enum Category{
	MONEY(Input.NICKEL, Input.DIME, Input.QUARTER, Input.DOLLAR),
	TIME_SELECTION(Input.TOOTHPASTE, Input.CHIPS, Input.SODA, Input.SOAP),
	QUIT_TRANSACTION(Input.ABORT_TRANSACTION),
	SHUT_DOWN(Input.STOP);
	
	private Input[] values;
	Category(Input...types){
		values = types;
	}
	
	private static EnumMap<Input, Category> categories = new EnumMap<Input, Category>(Input.class);
	static {
		for(Category c : Category.class.getEnumConstants()) {
			for(Input input : c.values) {
				categories.put(input, c);
			}
		}
	}
	
	public static Category categorize(Input input) {
		return categories.get(input);
	}
}
